package cn.tedu.charging.order.mqtt;

import cn.tedu.charging.common.pojo.JsonResult;
import cn.tedu.charging.common.pojo.param.ChargingProcessParam;
import cn.tedu.charging.common.pojo.param.GunStatusUpdateParam;
import cn.tedu.charging.common.pojo.vo.ChargingProcessVO;
import cn.tedu.charging.common.utils.JsonUtils;
import cn.tedu.charging.order.common.ChargingConstants;
import cn.tedu.charging.order.common.GunStatusConstants;
import cn.tedu.charging.order.common.MqttConstants;
import cn.tedu.charging.order.common.OrderStatusConstants;
import cn.tedu.charging.order.feign.CostClient;
import cn.tedu.charging.order.pojo.dto.ChargingProcessDto;
import cn.tedu.charging.order.pojo.dto.ChargingResultDto;
import cn.tedu.charging.order.pojo.po.ChargingOrderFilePO;
import cn.tedu.charging.order.pojo.po.ChargingOrderSuccessPO;
import cn.tedu.charging.order.pojo.po.ChargingProcessPO;
import lombok.extern.slf4j.Slf4j;
import org.eclipse.paho.client.mqttv3.*;
import org.springframework.beans.BeanUtils;

@Slf4j
public class MqttConsumer implements MqttCallbackExtended {

    private MqttClient mqttClient;
    private MqttContext mqttContext;

    public MqttConsumer(MqttClient mqttClient,MqttContext mqttContext) {
        this.mqttClient = mqttClient;
        this.mqttContext = mqttContext;
    }

    @Override
    public void connectComplete(boolean b, String s) {
        // 建立连接的回调
        log.debug("回调成功，连接成功:b:{},s:{}", b, s);
        //订阅topic
        //设备返回的开始充电响应结果
        String topicChargingResult = MqttConstants.TOPIC_CHARGING_RESULT;
        //设备返回的充电进度
        String topicChargingProcess = MqttConstants.TOPIC_CHARGING_PROCESS;
        try {
            log.debug("订阅设备返回的开始充电响应结果topic:{}", topicChargingResult);
            mqttClient.subscribe(topicChargingResult);
            log.debug("订阅设备返回的开始充电响应结果topic:{},成功", topicChargingResult);

            log.debug("订阅设备返回的充电进度topic:{}", topicChargingProcess);
            mqttClient.subscribe(topicChargingProcess);
            log.debug("订阅设备返回的充电进度topic:{},成功", topicChargingProcess);
        } catch (MqttException e) {
            //打印异常信息，有可能需要进行重连，如果重连失败，抛异常，监控系统 告警
            throw new RuntimeException(e);
        }
    }

    @Override
    public void connectionLost(Throwable throwable) {
        log.debug("connectionLost 连接断开:{}", throwable);
    }

    @Override
    public void messageArrived(String topic, MqttMessage mqttMessage) throws Exception {
        log.debug("收到设备发送的消息,topic:{},消息:{}", topic, mqttMessage);

        if (MqttConstants.TOPIC_CHARGING_RESULT.equals(topic)){
            log.debug("收到设备发送的开始充电响应结果");
            handleChargingResult(topic,mqttMessage);
        }else if (MqttConstants.TOPIC_CHARGING_PROCESS.equals(topic)){
            log.debug("收到设备发送的充电进度");
            handleChargingProcess(topic,mqttMessage);
        }


    }

    /**
     * 处理充电的进度数据
     * @param topic
     * @param mqttMessage
     */
    private void handleChargingProcess(String topic,MqttMessage mqttMessage) {
        // 处理消息 将消息内容  转化为对象
        ChargingProcessDto chargingProcessDto = null;
        try {
            chargingProcessDto = JsonUtils.fromJson(mqttMessage.toString(), ChargingProcessDto.class);
        }catch (Exception e){
            log.error("解析消息失败:{}，设备消息无法转换为Json对象,chargingProcessDto:{}",chargingProcessDto, mqttMessage, e);
        }
        log.debug("设备发送的消息，转换为java对象 chargingProcessDto:{}", chargingProcessDto);
        if (chargingProcessDto != null){
            //TODO:处理进度
            // 1. 获取计费的结果
            ChargingProcessVO chargingProcessVO = getCostChargingProcessVO(chargingProcessDto);
            // 2. 保存计费的结果 和同步的数据  到ES
            saveChargingProcessData2ES(chargingProcessVO,chargingProcessDto);
            // 3. 返回给用户 通知用户
            sendChargingProcess2User(chargingProcessVO);


            //4 判断用户余额是否足够
            //   4.1 订单服务通过计价服务计算订单价格
            //   4.2 订单服务调用用户服务 通过用户id获取用户的余额 userClient 通过open-feign
            //   4.3 判断用户余额是否足够 用户余额 是否 大于 当前的订单价格
            //   4.4 如果大于 继续充电
            //   4.5 如果小于 提醒用户，充值，停止充电 给设备发送消息 停止充电
            //5 判断是否充满
            //   5.1 订单服务收到设备同步的充电进度数据
            //   5.2 判断是否充满
            //   5.3 如果充满
            //       通知用户 推送用户的账单 花了多少钱，充了多少度电，用了多长时间，余额还有多少
            //       修改订单状态为 已经完成
            //       扣除用户余额
            //      修改枪的状态为空
            //      可以扩张 给用户增加积分，发送优惠券
            //      无需通知设备停止充电，充满后设备会自己停止充电
            //  5.4 没充满继续
            //6 判断设备温度
            //  6.1 订单服务收到设备同步的温度数据
            //  6.2 判断温度是否正常 分级 20度-30度 大于30度 可以继续充 让用户选择 如果大于50 强制停止充电
            //  6.3 如果不正常，通知用户，停止充电，给设备发送停止充电的消息
            //  6.4 如果正常，继续充电
        }

    }

    private void sendChargingProcess2User(ChargingProcessVO chargingProcessVO) {
        // 1. 获取用户id
        // 2. 通过用户id获取用户信息
        // 3. 拼接消息
        // 4. 发送消息
        log.debug("发送消息给用户:{}",chargingProcessVO);
        String jsonProcessVO = JsonUtils.toJson(chargingProcessVO);
        log.debug("推送消息给用户:{}",jsonProcessVO);
        mqttContext.getWebSocketServer().sendMessage(chargingProcessVO.getUserId(),jsonProcessVO);
        log.debug("推送消息给用户:{},成功,消息:{}",chargingProcessVO.getUserId(),jsonProcessVO);
    }

    /**
     * 保存计费的结果 和同步的数据  到ES
     * @param chargingProcessVO
     * @param chargingProcessDto
     */
    private void saveChargingProcessData2ES(ChargingProcessVO chargingProcessVO,ChargingProcessDto chargingProcessDto) {
        log.debug("保存计费的结果 和同步的数据  到ES chargingProcessVO:{},chargingProcessDto:{}",chargingProcessVO,chargingProcessDto);
        ChargingProcessPO chargingProcessPO = new ChargingProcessPO();
        BeanUtils.copyProperties(chargingProcessDto,chargingProcessPO);
        BeanUtils.copyProperties(chargingProcessVO,chargingProcessPO);
        log.debug("数据赋值,验证:{}",chargingProcessPO);
        chargingProcessPO.setId(System.currentTimeMillis());
        // 保存数据
        log.debug("保存数据到ES chargingProcessPO:{}",chargingProcessPO);
        mqttContext.getChargingProcessESRepository().save(chargingProcessPO);
        log.debug("保存数据到ES chargingProcessPO:{}成功",chargingProcessPO);
    }

    /**
     * 获取计费的结果
     * @param chargingProcessDto
     * @return
     */
    private ChargingProcessVO getCostChargingProcessVO(ChargingProcessDto chargingProcessDto) {
        ChargingProcessParam chargingProcessParam = new ChargingProcessParam();
        log.debug("开始复制属性 chargingProcessDto:{}", chargingProcessDto);
        BeanUtils.copyProperties(chargingProcessDto,chargingProcessParam);
        log.debug("开始计算费用 chargingProcessParam:{}",chargingProcessParam);
        CostClient costClient = mqttContext.getCostClient();
        JsonResult<ChargingProcessVO> chargingProcessVOJsonResult = costClient.calculateCost(chargingProcessParam);
        log.debug("调用计费服务，计算费用入参, chargingProcessParam:{},出参:{}",chargingProcessParam,chargingProcessVOJsonResult.getData());
        if (chargingProcessVOJsonResult != null) {
            return chargingProcessVOJsonResult.getData();
        }
        return null;
    }

    /**
     * 处理开始充电响应结果
     * @param topic
     * @param mqttMessage
     */
    private void handleChargingResult(String topic,MqttMessage mqttMessage) {
        // 处理消息 将消息内容  转化为对象
        ChargingResultDto chargingResultDto = null;
        try {
            chargingResultDto = JsonUtils.fromJson(mqttMessage.toString(), ChargingResultDto.class);
        }catch (Exception e){
            log.error("解析消息失败:{}，设备消息无法转换为Json对象,chargingResultDto:{}",chargingResultDto, mqttMessage, e);
        }
        log.debug("设备发送的消息，转换为java对象 ChargingResultDto:{}", chargingResultDto);
        if (chargingResultDto != null){
            String result = chargingResultDto.getResult();
            if (ChargingConstants.CHARGING_RESULT_SUCCESS.equals(result)){
                log.debug("设备发送的消息，是充电成功的消息");
                // 保存到数据库
                ChargingOrderSuccessPO chargingOrderSuccessPO = new ChargingOrderSuccessPO();
                chargingOrderSuccessPO.setBillId(chargingResultDto.getOrderNo());
                chargingOrderSuccessPO.setGunId(chargingResultDto.getGunId());
                // 订单收到设备 开始充电成功的消息 保存成功订单记录 订单的状态为 充电中
                chargingOrderSuccessPO.setBillStatus(OrderStatusConstants.ORDER_STATUS_PROCESS);
                log.debug("保存成功订单记录:{}",chargingOrderSuccessPO);
                Integer row = mqttContext.getOrderSuccessMapper().insert(chargingOrderSuccessPO);
                log.debug("保存成功订单记录，受影响行数:{}",row);

            }else {
                //TODO:处理失败
                log.debug("设备发送的消息，是充电失败的消息");
                ChargingOrderFilePO chargingOrderFilePO =new ChargingOrderFilePO();
                chargingOrderFilePO.setBillId(chargingResultDto.getOrderNo());
                chargingOrderFilePO.setFailDesc(chargingResultDto.getResult()+"设备有响应,但是开始充电失败");
                log.debug("保存失败订单记录:{}",chargingOrderFilePO);
                Integer row = mqttContext.getOrderFileMapper().insert(chargingOrderFilePO);
                log.debug("保存失败订单记录，受影响行数:{}",row);
                log.debug("设备发送的消息，是充电失败的消息,通知用户 "); //todo
                log.debug("给用户发送优惠券 表示歉意 给用户心灵上的安慰");
                log.debug("通知设备管理员 进行设备的检修");
                log.debug("修改枪的状态 为 故障");
                // 修改抢的状态
                log.debug("修改抢的状态:{}",chargingResultDto.getGunId());
                GunStatusUpdateParam gunStatusUpdateParam = new GunStatusUpdateParam();
                gunStatusUpdateParam.setGunId(chargingResultDto.getGunId());
                gunStatusUpdateParam.setStatus(GunStatusConstants.GUN_STATUS_FAIL);
                Boolean success = mqttContext.getDeviceClient().updateGunStatus(gunStatusUpdateParam).getData();
                log.debug("修改抢的状态为故障，是否成功:{}",success);

            }
            }
    }

    @Override
    public void deliveryComplete(IMqttDeliveryToken iMqttDeliveryToken) {
        log.debug("消息发送完成");
    }
}
